package city.spring.modules.work.controller;

import city.spring.modules.system.mapstruct.SimpleUserVoMapStruct;
import city.spring.modules.system.service.UserService;
import city.spring.modules.system.vo.SimpleUserVO;
import city.spring.modules.work.entity.WorkScheduleEntity;
import city.spring.modules.work.entity.WorkScheduleItemEntity;
import city.spring.modules.work.entity.WorkScheduleShiftEntity;
import city.spring.modules.work.repository.WorkScheduleItemRepository;
import city.spring.modules.work.service.WorkScheduleItemService;
import city.spring.modules.work.service.WorkScheduleService;
import city.spring.modules.work.service.WorkScheduleShiftService;
import city.spring.modules.work.vo.WorkScheduleItemVO;
import city.spring.modules.work.vo.WorkScheduleVO;
import city.spring.utils.BeanUtils;
import city.spring.utils.MyBatisUtils;
import city.spring.utils.OrganizationUtils;
import city.spring.utils.Pair;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ArrayListMultimap;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Controller：上班计划表（排班结果主体记录）
 *
 * @author 侯坤林
 * @date 2020-06-04 16:43:43
 */
@RestController
@RequestMapping("/work_schedule")
public class WorkScheduleController extends ApiController {
    private final WorkScheduleService workScheduleService;
    private final WorkScheduleItemService workScheduleItemService;
    private final WorkScheduleItemRepository workScheduleItemRepository;
    private final WorkScheduleShiftService workScheduleShiftService;
    private final UserService userService;
    private final SimpleUserVoMapStruct simpleUserVoMapStruct;
    /**
     * 实体类可排序字段
     */
    private final List<SFunction<WorkScheduleEntity, ?>> entityOrderFields;

    public WorkScheduleController(WorkScheduleService workScheduleService, WorkScheduleItemService workScheduleItemService, WorkScheduleItemRepository workScheduleItemRepository, WorkScheduleShiftService workScheduleShiftService, UserService userService, SimpleUserVoMapStruct simpleUserVoMapStruct) {
        this.workScheduleService = workScheduleService;
        this.workScheduleItemService = workScheduleItemService;
        this.workScheduleItemRepository = workScheduleItemRepository;
        this.workScheduleShiftService = workScheduleShiftService;
        this.userService = userService;
        this.simpleUserVoMapStruct = simpleUserVoMapStruct;
        entityOrderFields = new ArrayList<>();
        entityOrderFields.add(WorkScheduleEntity::getId);
        entityOrderFields.add(WorkScheduleEntity::getWorkPlanId);
        entityOrderFields.add(WorkScheduleEntity::getTitle);
        entityOrderFields.add(WorkScheduleEntity::getStartDate);
        entityOrderFields.add(WorkScheduleEntity::getEndDate);
        entityOrderFields.add(WorkScheduleEntity::getIsEnabled);
        entityOrderFields.add(WorkScheduleEntity::getVersion);
        entityOrderFields.add(WorkScheduleEntity::getGmtCreated);
        entityOrderFields.add(WorkScheduleEntity::getGmtModified);
    }

    /**
     * 获取全部的 <strong>上班计划表（排班结果主体记录）</strong> 列表
     */
    @GetMapping("all")
    public Object listAll(@PageableDefault Pageable pageable, WorkScheduleEntity entity) {
        LambdaQueryChainWrapper<WorkScheduleEntity> lambdaQuery = buildLambdaQuery(entity);
        OrganizationUtils.includes(lambdaQuery, WorkScheduleEntity::getOrgId);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        return success(lambdaQuery.list());
    }

    /**
     * 分页获取 <strong>上班计划表（排班结果主体记录）</strong> 列表
     *
     * @param pageable 分页参数信息
     */
    @GetMapping
    public Object list(@PageableDefault Pageable pageable, WorkScheduleEntity entity) {
        LambdaQueryChainWrapper<WorkScheduleEntity> lambdaQuery = buildLambdaQuery(entity);
        OrganizationUtils.includes(lambdaQuery, WorkScheduleEntity::getOrgId);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        Page<WorkScheduleEntity> page = lambdaQuery.page(MyBatisUtils.toPage(pageable, false));
        return success(page);
    }

    /**
     * 获取今日同部门的上班人员和相关信息列表（今日的同部门的排班的计划上班人员）
     *
     * @param userId 限制查询今日的某个用户的排班信息
     */
    @GetMapping("todayScheduleItem")
    public Object getTodayScheduleItem(String userId, LocalDate date) {
        if (date == null) {
            date = LocalDate.now();
        }
        LambdaQueryChainWrapper<WorkScheduleEntity> lambdaQuery = workScheduleService.lambdaQuery();
        OrganizationUtils.includes(lambdaQuery, WorkScheduleEntity::getOrgId);
        // 查询指定日期内可用的排班结果主体记录
        lambdaQuery.ge(WorkScheduleEntity::getEndDate, date);
        lambdaQuery.le(WorkScheduleEntity::getStartDate, date);
        lambdaQuery.eq(WorkScheduleEntity::getIsEnabled, true);
        List<WorkScheduleEntity> scheduleEntityList = lambdaQuery.list();
        List<String> scheduleIds = scheduleEntityList.stream().map(WorkScheduleEntity::getId).collect(Collectors.toList());

        List<WorkScheduleItemVO> scheduleItemEntityList;

        if (scheduleIds.isEmpty()) {
            scheduleItemEntityList = Collections.emptyList();
        } else {
            QueryWrapper<WorkScheduleItemEntity> queryWrapper = new QueryWrapper<>();
            // 限制排班结果主体（涵盖限制组织机构）
            queryWrapper.in("work_schedule_id", scheduleIds)
                    // 限制指定用户
                    .eq(StringUtils.isNotBlank(userId), "user_id", userId)
                    // 限制指定日期
                    .ge("date", date)
                    .le("date", date.plusDays(7));
            scheduleItemEntityList = workScheduleItemRepository.getItems(queryWrapper);
        }

        ArrayListMultimap<String, WorkScheduleItemVO> multimap = ArrayListMultimap.create();
        for (WorkScheduleItemVO workScheduleItemEntity : scheduleItemEntityList) {
            multimap.put(workScheduleItemEntity.getWorkScheduleId(), workScheduleItemEntity);
        }

        // 组装返回结果信息
        List<WorkScheduleVO> result = new ArrayList<>();
        for (WorkScheduleEntity workScheduleEntity : scheduleEntityList) {
            String workScheduleEntityId = workScheduleEntity.getId();
            WorkScheduleVO vo = BeanUtils.toChildren(workScheduleEntity, WorkScheduleVO.class);
            vo.setItems(multimap.get(workScheduleEntityId));
            vo.setShifts(workScheduleShiftService.getScheduleShifts(workScheduleEntityId));
            result.add(vo);
        }

        return success(result);
    }

    /**
     * 获取一个 <strong>上班计划表（排班结果主体记录）</strong>
     *
     * @param id 主键ID
     */
    @GetMapping("{id}")
    public Object info(@PathVariable String id) {
        return success(workScheduleService.getById(id));
    }

    /**
     * 获取一个 <strong>排班表</strong> 的具体排班信息
     *
     * @param id 主键ID
     */
    @GetMapping("{id}/items")
    public Object items(@PathVariable String id) {
        Map<String, Object> map = new HashMap<>();
        map.put("items", workScheduleItemRepository.getScheduleItems(id, true));
        map.put("shifts", workScheduleShiftService.getScheduleShifts(id));
        return success(map);
    }

    /**
     * 获取一个 <strong>上班计划表（排班结果主体记录）</strong> 的详细信息（含有上班班次、上班记录信息）
     *
     * @param id 主键ID
     */
    @GetMapping("{id}/details")
    public Object details(@PathVariable String id) {
        return success(workScheduleService.getWorkSchedule(id));
    }

    /**
     * 获取一个 <strong>上班计划表（排班结果主体记录）</strong> 的统计信息
     *
     * @param id 主键ID
     */
    @GetMapping("{id}/statistics")
    public Object statistics(@PathVariable String id) {
        // 当前排班结果（排班表）的班次列表
        List<WorkScheduleShiftEntity> shiftEntities = workScheduleShiftService.lambdaQuery()
                .eq(WorkScheduleShiftEntity::getWorkScheduleId, id)
                .list();
        // 当前排版结果的人员上班记录信息列表
        List<WorkScheduleItemVO> scheduleItems = workScheduleItemRepository.getScheduleItems(id, true);
        // 获取这个上班列表中的所有人员信息
        Set<String> userIds = scheduleItems.stream().map(WorkScheduleItemEntity::getUserId).collect(Collectors.toSet());
        List<SimpleUserVO> simpleUsers;
        if (userIds.isEmpty()) {
            simpleUsers = Collections.emptyList();
        } else {
            simpleUsers = userService.listByIds(userIds).stream().map(simpleUserVoMapStruct::toVo).collect(Collectors.toList());
        }

        // 获得班次的上班人数次数
        List<Pair<WorkScheduleShiftEntity, Long>> shifts = shiftEntities.stream().map(shift -> {
            long count = scheduleItems.stream().filter(item -> shift.getId().equals(item.getWorkScheduleShiftId())).count();
            return Pair.create(shift, count);
        }).collect(Collectors.toList());
        // 获得用户的上班次数
        List<Pair<SimpleUserVO, List<Pair<String, Long>>>> users = simpleUsers.stream().map(user -> {
            // 获得用户的班次上班次数
            List<Pair<String, Long>> list = shiftEntities.stream().map(shift -> {
                long count = scheduleItems.stream().filter(item -> user.getId().equals(item.getUserId()) && shift.getId().equals(item.getWorkScheduleShiftId())).count();
                return Pair.create(shift.getId(), count);
            }).collect(Collectors.toList());
            return Pair.create(user, list);
        }).collect(Collectors.toList());

        return success(new HashMap<String, Object>() {{
            put("shifts", shifts);
            put("users", users);
        }});
    }

    /**
     * 添加一个 <strong>上班计划表（排班结果主体记录）</strong>
     *
     * @param entity 修改后的信息
     */
    @PostMapping
    public Object add(@RequestBody WorkScheduleEntity entity) {
        workScheduleService.saveWorkSchedule(entity);

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.LOCATION, String.valueOf(entity.getId()));
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }

    /**
     * 修改一个 <strong>上班计划表（排班结果主体记录）</strong>
     *
     * @param id     主键ID
     * @param entity 修改后的信息
     */
    @PutMapping("{id}")
    public Object update(@PathVariable String id, @RequestBody WorkScheduleEntity entity) {
        entity.setId(id);
        workScheduleService.updateWorkSchedule(entity);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 删除一个 <strong>上班计划表（排班结果主体记录）</strong>
     *
     * @param id 主键ID
     */
    @DeleteMapping("{id}")
    public Object delete(@PathVariable String id) {
        workScheduleService.deleteWorkSchedule(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    /**
     * 删除多个 <strong>上班计划表（排班结果主体记录）</strong>
     *
     * @param ids 主键ID列表
     */
    @DeleteMapping
    public Object deleteIds(@RequestBody List<String> ids) {
        workScheduleService.deleteWorkSchedule(ids);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    /**
     * 构建查询条件内容
     *
     * @param entity 实体对象
     * @return lambda query chain wrapper
     */
    private LambdaQueryChainWrapper<WorkScheduleEntity> buildLambdaQuery(WorkScheduleEntity entity) {
        LambdaQueryChainWrapper<WorkScheduleEntity> lambdaQuery = workScheduleService.lambdaQuery();
        lambdaQuery.eq(StringUtils.isNotBlank(entity.getId()), WorkScheduleEntity::getId, entity.getId());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getWorkPlanId()), WorkScheduleEntity::getWorkPlanId, entity.getWorkPlanId());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getTitle()), WorkScheduleEntity::getTitle, entity.getTitle());
        lambdaQuery.eq(entity.getStartDate() != null, WorkScheduleEntity::getStartDate, entity.getStartDate());
        lambdaQuery.eq(entity.getEndDate() != null, WorkScheduleEntity::getEndDate, entity.getEndDate());
        lambdaQuery.eq(entity.getIsEnabled() != null, WorkScheduleEntity::getIsEnabled, entity.getIsEnabled());
        lambdaQuery.eq(entity.getVersion() != null, WorkScheduleEntity::getVersion, entity.getVersion());
        lambdaQuery.eq(entity.getGmtCreated() != null, WorkScheduleEntity::getGmtCreated, entity.getGmtCreated());
        lambdaQuery.eq(entity.getGmtModified() != null, WorkScheduleEntity::getGmtModified, entity.getGmtModified());
        return lambdaQuery;
    }

}
